#!/usr/bin/env python
# 基于UDP的聊天室程序：服务端
# https://www.it610.com/article/1279578930843500544.htm
import socket


# 创建网络连接
def main():
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建UDP套接字
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    address = (('127.0.0.1', 8080))  # 设置服务器地址
    server.bind(address)  # 绑定地址
    doRequest(server)  # 处理客户端请求


# 处理客户端请求
def doRequest(server):
    Users = {}  # 用户数据字典，键为用户名，值为列表，包含登录密码、在线状态、地址
    while True:
        # 接收客户端的消息
        msg, addr = server.recvfrom(1024)
        msglist = msg.decode().split(' ')  # 拆分数据
        if msglist[0] == '1':  # 注册
            doRegister(server, Users, msglist[1], msglist[2], addr)
        elif msglist[0] == '2':  # 登录
            doLogin(server, Users, msglist[1], msglist[2], addr)
        elif msglist[0] == '3':  # 公聊
            content = ' '.join(msglist[2:])  # 获取完整发送内容
            doChat(server, Users, msglist[1], content)
        elif msglist[0] == '4':  # 私聊
            content = ' '.join(msglist[3:])  # 获取完整发送内容
            doPrivateChat(server, Users, msglist[1], msglist[2], content)
        elif msglist[0] == '5':  # 退出
            doQuit(server, Users, msglist[1])
        else:
            server.sendto('发生未知错误'.encode(), addr)


# 注册
def doRegister(server, Users, name, password, addr):
    # 判断用户名是否已存在
    if name in Users.keys():
        server.sendto('用户名已存在'.encode(), addr)
        return
    # 该用户名未注册过，将该用户名加入用户数据字典，并设为在线状态
    Users[name] = [password, 1, addr]
    # 通知客户端登录成功
    server.sendto('OK'.encode(), addr)
    # 通知其他所有用户
    message = '\n' + name + '进入聊天室'
    for user in Users.keys():
        if user != name:
            server.sendto(message.encode(), Users[user][2])
    # 服务器打印查看数据
    print(message)
    print(Users)


# 登录
def doLogin(server, Users, name, password, addr):
    # 判断该用户名是否已注册
    if name not in Users.keys():
        server.sendto('用户名不存在，请先注册'.encode(), addr)
        return
    # 判断密码是否正确
    if Users[name][0] != password:
        server.sendto('密码错误，请重试'.encode(), addr)
        return
    # 判断该用户名是否已登录
    if Users[name][1] == 1:
        server.sendto('该用户已登陆'.encode(), addr)
        return
    # 满足登录条件，标注在线状态
    Users[name][1] = 1
    Users[name][2] = addr
    # 通知客户端登录成功
    server.sendto('OK'.encode(), addr)
    # 通知其他所有用户
    message = '\n' + name + '进入聊天室'
    for user in Users.keys():
        if user != name:
            server.sendto(message.encode(), Users[user][2])
    # 服务器打印查看数据
    print(message)
    print(Users)


# 公聊
def doChat(server, Users, name, content):
    message = '\n[公聊]' + name + '：' + content
    for user in Users.keys():
        if user != name:  # 发信息给其他所有用户
            server.sendto(message.encode(), Users[user][2])


# 私聊
def doPrivateChat(server, Users, name, object, content):
    # 为了看得更清楚，添加5个tab，使私聊部分在右侧显示
    message = '\n                   [私聊]' + name + '：' + content
    for user in Users.keys():
        if user == object:  # 发给目标用户
            server.sendto(message.encode(), Users[user][2])


# 退出
def doQuit(server, Users, name):
    # 标注离线状态
    Users[name][1] = 0
    message = '\n' + name + '退出聊天室'
    for user in Users.keys():
        if user == name:
            server.sendto('exit'.encode(), Users[user][2])
        else:
            server.sendto(message.encode(), Users[user][2])
    # 服务器打印查看数据
    print(message)
    print(Users)


if __name__ == '__main__':
    main()
